package com.jl.magic.images;

import com.jl.exceptions.ImageDealException;
import com.jl.magic.enums.AshEnum;

import java.awt.image.BufferedImage;

import static com.jl.magic.enums.AshEnum.*;

/**
 * @author jiangli
 * @create 2020/7/16 4:19 下午
 */
public class Ashing {

    /**
     * 图片灰化处理
     *
     * @param image   BufferedImage
     * @param ashEnum 灰化类型
     * @return 灰化后的BufferedImage
     */
    public static BufferedImage ash(BufferedImage image, AshEnum ashEnum) throws ImageDealException {
        int width = image.getWidth();
        int height = image.getHeight();

        switch (ashEnum) {
            case Gray:
                BufferedImage bfi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
                for (int y = 0; y < height; y++) {
                    for (int x = 0; x < width; x++) {
                        bfi.setRGB(x, y, image.getRGB(x, y));
                    }
                }
                return bfi;
            case Red:
            case Green:
            case Blue:
            case RgbMax:
            case RgbMin:
            case RgbAvg:
            case RgbWeight:
            case OptimizedWeight:
                for (int y = 0; y < image.getHeight(); y++) {
                    for (int x = 0; x < image.getWidth(); x++) {
                        int pixel = image.getRGB(x, y);
                        int[] rgb = BaseTools.rgb(pixel);
                        switch (ashEnum) {
                            case Red:
                                pixel = replaceChannel(pixel, rgb[0], Red);
                                break;
                            case Green:
                                pixel = replaceChannel(pixel, rgb[1], Green);
                                break;
                            case Blue:
                                pixel = replaceChannel(pixel, rgb[2], Blue);
                                break;
                            case RgbMax:
                                int max = Math.max(rgb[0], Math.max(rgb[1], rgb[2]));
                                if (max == rgb[0]) {
                                    pixel = replaceChannel(pixel, rgb[0], Red);
                                } else if (max == rgb[1]) {
                                    pixel = replaceChannel(pixel, rgb[1], Green);
                                } else {
                                    pixel = replaceChannel(pixel, rgb[2], Blue);
                                }
                                break;
                            case RgbMin:
                                int min = Math.min(rgb[0], Math.min(rgb[1], rgb[2]));
                                if (min == rgb[0]) {
                                    pixel = replaceChannel(pixel, rgb[0], Red);
                                } else if (min == rgb[1]) {
                                    pixel = replaceChannel(pixel, rgb[1], Green);
                                } else {
                                    pixel = replaceChannel(pixel, rgb[2], Blue);
                                }
                                break;
                            case RgbAvg:
                                int avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
                                pixel = replaceChannel(pixel, avg, RgbAvg);
                                break;
                            case RgbWeight:
                                /*
                                  0.299f * r + 0.587f * g + 0.114f * b
                                 */
                                int weight = (int) (0.299f * rgb[0] + 0.587f * rgb[1] + 0.114f * rgb[2]);
                                pixel = replaceChannel(pixel, weight, RgbWeight);
                                break;
                            case OptimizedWeight:
                                /*
                                  可通过将RgbWeight算法的系数扩大1000倍来消除浮点运算，然后再将结果除以1000来提升效率
                                  又由于位运算又比除法运算快很多，故可以将系数扩大2的n次方倍，最后位移得到结果，效率会进一步提升；
                                  再将运算范围控制在16位运算以内，故n=7是最佳选择。
                                  0.299 * 128 = 38.272 = 38 + 0.272
                                  0.587 * 128 = 75.136 + 0.272 = 75.408 = 75 + 0.408
                                  0.114 * 128 = 14.592 + 0.408 = 15
                                 */
                                int optimizedWeight = (38 * rgb[0] + 75 * rgb[1] + 15 * rgb[2]) >> 7;
                                pixel = replaceChannel(pixel, optimizedWeight, OptimizedWeight);
                                break;
                            default:
                                throw new ImageDealException("灰化类型参数不合法");
                        }
                        image.setRGB(x, y, pixel);
                    }
                }
                return image;
            default:
                throw new ImageDealException("灰化类型参数不合法");
        }
    }

    /**
     * 用rgb某通道值替换其他通道值
     *
     * @param rgb    像素值
     * @param vector rgb某通道值
     * @param type   通道类型
     * @return 返回转换后的通道值
     */
    private static int replaceChannel(int rgb, int vector, AshEnum type) throws ImageDealException {
        switch (type) {
            case Red:
                rgb = ((vector << 8) & 0x0000ff00) | (rgb & 0xffff00ff);
                rgb = (vector & 0x000000ff) | (rgb & 0xffffff00);
                break;
            case Green:
                rgb = ((vector << 16) & 0x00ff0000) | (rgb & 0xff00ffff);
                rgb = (vector & 0x000000ff) | (rgb & 0xffffff00);
                break;
            case Blue:
                rgb = ((vector << 16) & 0x00ff0000) | (rgb & 0xff00ffff);
                rgb = ((vector << 8) & 0x0000ff00) | (rgb & 0xffff00ff);
                break;
            case RgbWeight:
            case RgbAvg:
            case OptimizedWeight:
                rgb = (vector << 16) & 0x00ff0000 | (rgb & 0xff00ffff);
                rgb = (vector << 8) & 0x0000ff00 | (rgb & 0xffff00ff);
                rgb = (vector) & 0x000000ff | (rgb & 0xffffff00);
                break;
            default:
                throw new ImageDealException("灰化类型参数不合法");
        }
        return rgb;
    }

}
